﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using MiscUtil;
using MiscUtil.Conversion;
using MiscUtil.IO;

namespace TlvLib
{
    public delegate void RawPacketHandler(object sender, RawPacketReceivedEventArgs args);

    public class RawPacketReceivedEventArgs : EventArgs<RawPacket>
    {
        public RawPacketReceivedEventArgs(RawPacket value) : base(value)
        {
            
        }

    }

    public delegate void StatusReportHandler(object sender, MiscUtil.EventArgs<string> args);

    public class TlvUnpacker
    {

        public static string ConnectSuccessMessage = "成功";

        private readonly string _host;
        private readonly int _port;

        private const int Headerlen = 4;
        private TcpClient _client;


        private object _sendLock = new object();


        public bool AutoReconnect { get; set; }
        public event RawPacketHandler RawPacketReceived;
        public event EventHandler<EventArgs<ConnectionStatus>> StatusReport;

        public void OnStatusReport(ConnectionStatus status)
        {
            var handler = StatusReport;
            if (handler != null) handler(this, new EventArgs<ConnectionStatus>(status));
        }

        private void RaiseRawPacketReceived(RawPacketReceivedEventArgs args)
        {
            RawPacketHandler handler = RawPacketReceived;
            if (handler != null) handler(this, args);
        }

        public TlvUnpacker(string host, int port)
        {
            _host = host;
            _port = port;
            AutoReconnect = true;
        }



        public void Start()
        {
            Connect();
        }

        public void Stop()
        {
            AutoReconnect = false;
            if (_client != null && _client.Connected)
            {
                _client.Close();
            }
        }

        public void Send(RawPacket packet)
        {
            lock (_sendLock)
            {
                if (_client != null && _client.Connected)
                {
                    var buffer = TlvPacker.Pack(packet);
                    try
                    {
                        _client.Client.Send(buffer);
                        _client.GetStream().Flush();
                    }
                    catch { }
                }
            }
        }

        private void Connect()
        {
            if (_client != null && _client.Connected)
            {
                _client.Close();
            }

            _client = new TcpClient();
            try
            {
                var msg = string.Format("开始连接 {0}:{1}", _host, _port);
                var a = new ConnectionStatus() {Description = msg, Status = Status.Progress};
                OnStatusReport(a);
                _client.BeginConnect(_host, _port, ConnectCallback, _client);
            }
            catch (Exception)
            {
                ReportConnectStatus(Status.Fail);
                DelayedReconnect();
            }
        }

        private void DelayedReconnect()
        {
            System.Threading.Thread.Sleep(5000);
            if (AutoReconnect)
            {
                Connect();
            }
        }

        private void ReportConnectStatus(Status status)
        {
            var msg = string.Format("连接 {0}:{1} {2}", _host, _port, status);
            var a = new ConnectionStatus() {Description = msg, Status = status};
            OnStatusReport(a);
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                _client.EndConnect(ar);
            }
            catch
            {
                ReportConnectStatus(Status.Fail);
                DelayedReconnect();
                return;
            }

           

            ReportConnectStatus(Status.Success);


            var stateObject = new StateObject();
            _client.GetStream().BeginRead(
                stateObject.Header,
                stateObject.HeaderBytesCount,
                stateObject.Header.Length - stateObject.HeaderBytesCount,
                ReadCallback, stateObject);
        }

        private void ReadCallback(IAsyncResult ar)
        {
            int bytesRead = 0;
            NetworkStream stream;
            var state = (StateObject)ar.AsyncState;
            try
            {
                stream = _client.GetStream();
                bytesRead = stream.EndRead(ar);
            }
            catch
            {
                ReportConnectStatus(Status.ConnectionBroken);
                DelayedReconnect();
                return;
            }

            if (state.IsReadingHeader)
            {
                state.HeaderBytesCount += bytesRead;
                if (state.HeaderBytesCount == state.Header.Length)
                {
                    state.IsReadingHeader = false;
                    var i = BitConverter.ToInt32(state.Header, 4);
                    var j = System.Net.IPAddress.NetworkToHostOrder(i);
                    state.Value = new byte[j];
                }
            }
            else
            {
                state.ValueBytesCount += bytesRead;
                if (state.Value.Length == state.ValueBytesCount)
                {
                    var i = BitConverter.ToInt16(state.Header, 2);
                    var j = System.Net.IPAddress.NetworkToHostOrder(i);
                    var p = new RawPacket()
                                {
                                    MessageId = j,
                                    Payload = state.Value
                                };

                   // ReportConnectStatus("收到数据包(1000-车牌，1003-连接状态，1007-校时回应), 包类型：" + j);
                    try
                    {
                        RaiseRawPacketReceived(new RawPacketReceivedEventArgs(p));
                    }
                    catch (Exception ex)
                    {
                        var msg = "处理数据包时发生异常：" + ex.Message;
                        ReportConnectStatus(Status.Exception);
                    }

                    state = new StateObject();
                    state.IsReadingHeader = true;
                }
            }

            try
            {
                if (state.IsReadingHeader)
                {
                    stream.BeginRead(
                                    state.Header,
                                    state.HeaderBytesCount,
                                    state.Header.Length - state.HeaderBytesCount,
                                    ReadCallback,
                                    state);
                }
                else
                {
                    stream.BeginRead(
                                    state.Value,
                                    state.ValueBytesCount,
                                    state.Value.Length - state.ValueBytesCount,
                                    ReadCallback,
                                    state);
                }
            }
            catch
            {
                ReportConnectStatus(Status.ConnectionBroken);
                DelayedReconnect();
            }

        }
    }


    class StateObject
    {
        public bool IsReadingHeader = true;

        public const int HeaderBytesLen = 8;
        public int HeaderBytesCount;
        public byte[] Header = new byte[HeaderBytesLen];


        public int ValueBytesCount;
        public byte[] Value;
    }

    public class ConnectionStatus
    {
        public Status Status;
        public Exception Exception;
        public string Description;
    }

    public enum Status
    {
        Progress, Success, Fail, ConnectionBroken, Exception
    }
}

